using System.Runtime.Remoting.Messaging;
using System.Web;
using System;
using System.Web.Caching;
using NHibernate;
using NHibernate.Cache;
using NHibernate.Cfg;
using ProjectBase.Utils;
using System.IO;
using System.Collections;
using CMS.Core.Domain;

namespace CMS.Data
{
    /// <summary>
    /// Handles creation and management of sessions and transactions.  It is a singleton because 
    /// building the initial session factory is very expensive. Inspiration for this class came 
    /// from Chapter 8 of Hibernate in Action by Bauer and King.  Although it is a sealed singleton
    /// you can use TypeMock (http://www.typemock.com) for more flexible testing.
    /// </summary>
    public sealed class NHibernateSessionManager
    {
        #region Thread-safe, lazy Singleton
        private System.Boolean _IsLookup = false;

        public System.Boolean IsLookup
        {
            get { return _IsLookup; }
            set { _IsLookup = value; }
        }

        /// <summary>
        /// This is a thread-safe, lazy singleton.  See http://www.yoda.arachsys.com/csharp/singleton.html
        /// for more details about its implementation.
        /// </summary>
        public static NHibernateSessionManager Instance
        {
            get
            {
                return Nested.NHibernateSessionManager;
            }
        }

        /// <summary>
        /// Initializes the NHibernate session factory upon instantiation.
        /// </summary>
        private NHibernateSessionManager()
        {
            InitSessionFactory();
        }

        /// <summary>
        /// Assists with ensuring thread-safe, lazy singleton
        /// </summary>
        private class Nested
        {
            static Nested() { }
            internal static readonly NHibernateSessionManager NHibernateSessionManager =
                new NHibernateSessionManager();
        }

        #endregion

        private void InitSessionFactory()
        {
            //sessionFactory = new Configuration().Configure().BuildSessionFactory();
            Configuration config = new NHibernate.Cfg.Configuration();

            config.Configure();
            config.Properties["connection.connection_string"] = CauHinhHeThong.ConnectionString;
            config.Properties["default_schema"] = CauHinhHeThong.ConnectionParameters[1] + ".dbo";
            sessionFactory = config.BuildSessionFactory();
        }

        /// <summary>
        /// Allows you to register an interceptor on a new session.  This may not be called if there is already
        /// an open session attached to the HttpContext.  If you have an interceptor to be used, modify
        /// the HttpModule to call this before calling BeginTransaction().
        /// </summary>
        public void RegisterInterceptor(IInterceptor interceptor)
        {
            ISession session = ContextSession;

            if (session != null && session.IsOpen)
            {
                throw new CacheException("You cannot register an interceptor once a session has already been opened");
            }

            GetSession(interceptor);
        }

        public ISession GetSession()
        {
            ISession Result = null;
            if (IsLookup)
                Result = GetSessionFrom("");
            else Result = GetSession(null);
            return Result; 
        }

        /// <summary>
        /// Gets a session with or without an interceptor.  This method is not called directly; instead,
        /// it gets invoked from other public methods.
        /// </summary>
        private ISession GetSession(IInterceptor interceptor)
        {
            ISession session = ContextSession;

            if (session == null)
            {
                if (interceptor != null)
                {
                    session = sessionFactory.OpenSession(interceptor);
                }
                else
                {
                    session = sessionFactory.OpenSession();
                }

                ContextSession = session;
            }

            Check.Ensure(session != null, "session was null");

            return session;
        }

        /// <summary>
        /// Flushes anything left in the session and closes the connection.
        /// </summary>
        public void CloseSession()
        {
            ISession session = ContextSession;

            if (session != null && session.IsOpen)
            {
                try
                {
                    session.Flush();
                    session.Close();
                }
                catch (HibernateException ex)
                {

                }
            }

            ContextSession = null;
        }

        public void BeginTransaction()
        {
            ITransaction transaction = ContextTransaction;

            if (transaction == null)
            {
                try
                {
                    transaction = GetSession().BeginTransaction();
                    ContextTransaction = transaction;
                }catch(Exception ex)
                {
                
                }
            }
        }

        public void CommitTransaction()
        {
            ITransaction transaction = ContextTransaction;

            try
            {
                if (HasOpenTransaction())
                {
                    transaction.Commit();
                    ContextTransaction = null;
                }
            }
            catch (HibernateException)
            {
                RollbackTransaction();
                throw;
            }
        }

        public bool HasOpenTransaction()
        {
            ITransaction transaction = ContextTransaction;

            return transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack;
        }

        public void RollbackTransaction()
        {
            ITransaction transaction = ContextTransaction;

            try
            {
                if (HasOpenTransaction())
                {
                    transaction.Rollback();
                }

                ContextTransaction = null;
            }
            finally
            {
                CloseSession();
            }
        }

        private ITransaction ContextTransaction
        {
            get
            {
                if (IsInWebContext())
                {
                    return (ITransaction)HttpContext.Current.Items[TRANSACTION_KEY];
                }
                else
                {
                    return (ITransaction)CallContext.GetData(TRANSACTION_KEY);
                }
            }
            set
            {
                if (IsInWebContext())
                {
                    HttpContext.Current.Items[TRANSACTION_KEY] = value;
                }
                else
                {
                    CallContext.SetData(TRANSACTION_KEY, value);
                }
            }
        }

        /// <summary>
        /// If within a web context, this uses <see cref="HttpContext" /> instead of the WinForms 
        /// specific <see cref="CallContext" />.  Discussion concerning this found at 
        /// http://forum.springframework.net/showthread.php?t=572.
        /// </summary>
        private ISession ContextSession
        {
            get
            {
                if (IsInWebContext())
                {
                    return (ISession)HttpContext.Current.Items[SESSION_KEY];
                }
                else
                {
                    return (ISession)CallContext.GetData(SESSION_KEY);
                }
            }
            set
            {
                if (IsInWebContext())
                {
                    HttpContext.Current.Items[SESSION_KEY] = value;
                }
                else
                {
                    CallContext.SetData(SESSION_KEY, value);
                }
            }
        }

        private bool IsInWebContext()
        {
            return HttpContext.Current != null;
        }

        private const string TRANSACTION_KEY = "CONTEXT_TRANSACTION";
        private const string SESSION_KEY = "CONTEXT_SESSION";
        private ISessionFactory sessionFactory;

        #region Custommize Methods
        public ISession GetSessionFrom(string sessionFactoryConfigPath)
        {
            //sessionFactoryConfigPath = @"D:\Cac du an\Duan_2008\HSCV\SourceCode\CMS.DOMS\CMS.Web\Config\LookupDb.config";
            //sessionFactoryConfigPath = CauHinhHeThong.LookupPath;
            ISession session = null;// (ISession)contextSessions[sessionFactoryConfigPath];

            if (session == null)
            {
                
                    session =
                     GetSessionFactoryFor(sessionFactoryConfigPath).OpenSession();

                contextSessions[sessionFactoryConfigPath] = session;
            }

            if (session == null)
                // It would be more appropriate to throw
                // a more specific exception than ApplicationException

                throw new ApplicationException("session was null");

            return session;
        }

        private Hashtable contextSessions
        {
            get
            {
                if (CallContext.GetData("CONTEXT_SESSIONS") == null)
                {
                    CallContext.SetData("CONTEXT_SESSIONS", new Hashtable());
                }

                return (Hashtable)CallContext.GetData("CONTEXT_SESSIONS");
            }
        }

        private ISessionFactory GetSessionFactoryFor(string sessionFactoryConfigPath)
        {
            if (string.IsNullOrEmpty(sessionFactoryConfigPath))
                throw new ArgumentNullException("sessionFactoryConfigPath" +
                          " may not be null nor empty");

            //  Attempt to retrieve a cached SessionFactory from the HttpRuntime's cache.
            ISessionFactory sessionFactory = null;
              //(ISessionFactory)HttpRuntime.Cache.Get(sessionFactoryConfigPath);

            //  Failed to find a cached SessionFactory so make a new one.
            if (sessionFactory == null)
            {
                if (!File.Exists(sessionFactoryConfigPath))
                    // It would be more appropriate to throw
                    // a more specific exception than ApplicationException

                    throw new ApplicationException(
                        "The config file at '" + sessionFactoryConfigPath +
                        "' could not be found");

                NHibernate.Cfg.Configuration cfg = new NHibernate.Cfg.Configuration();
                cfg.Configure(sessionFactoryConfigPath);

                //  Now that we have our Configuration object, create a new SessionFactory

                sessionFactory = cfg.BuildSessionFactory();

                if (sessionFactory == null)
                {
                    throw new InvalidOperationException(
                      "cfg.BuildSessionFactory() returned null.");
                }

                HttpRuntime.Cache.Add(sessionFactoryConfigPath,
                            sessionFactory, null, DateTime.Now.AddDays(7),
                    TimeSpan.Zero, CacheItemPriority.High, null);
            }

            return sessionFactory;
        }
        #endregion
    }
}
